#ifndef __CE_TESTER_H__
#define __CE_TESTER_H__

#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>

#include <list>
#include <string>
#include <iostream>

#include "ce_tester_case.h"
#include "ce_tester_record.h"
#include "ce_tester_report.h"

namespace CETester {

#define RunAllTester gTcList.RunAllTesterCase 
#define ErrorLen     1024

#define CE_TESTCASE(name)                                               \
class TesterCase ## name: public TesterCase                             \
{                                                                       \
public:                                                                 \
    TesterCase ## name(): TesterCase(#name, __FILE__, __LINE__) {       \
        gTcList.AddTesterCase(this);                                    \
        gTc = this;                                                     \
    }                                                                   \
                                                                        \
    virtual void RunCase() override;                                    \
                                                                        \
private:                                                                \
    virtual void TesterCaseImpl();                                      \
};                                                                      \
                                                                        \
static TesterCase ## name tc ## name;                                   \
                                                                        \
void TesterCase ## name::RunCase() {                                    \
    std::cout << "  Start run tester case: " << mTCName                 \
              << " (" << mTCFile << ":" << mTCLine << ")" << std::endl; \
                                                                        \
    TesterReport report(mTCRecords);                                    \
    gTc = this;                                                         \
                                                                        \
                                                                        \
    TesterCaseImpl();                                                   \
}                                                                       \
                                                                        \
void TesterCase ## name::TesterCaseImpl()

#define TestPrint(format, ...)                                          \
    printf("    ");                                                     \
    printf(format"\n", ##__VA_ARGS__);                                      \

#define TestErrorf(format, ...) {                                       \
    char err[ErrorLen];                                                 \
    snprintf(err, ErrorLen, format, ##__VA_ARGS__);                     \
    gTc->AddRecord(new TesterRecord(std::string(err), __LINE__, false));\
}

#define EXPECT_EQ(v1, v2)                                               \
    gTc->AddRecord(new TesterRecord(std::string(#v1)+"=="+#v2, __LINE__, v1==v2))

#define EXPECT_NE(v1, v2)                                               \
    gTc->AddRecord(new TesterRecord(std::string(#v1)+"!="+#v2, __LINE__, v1!=v2))

#define EXPECT_CSTR_EQ(s1, s2)                                          \
    gTc->AddRecord(new TesterRecord(std::string(#s1)+"=="+#s2, __LINE__, \
    strlen((char*)s1)==strlen((char*)s2) && memcmp(s1, s2, strlen((char*)s1)) == 0))

#define EXPECT_CSTR_NE(s1, s2)                                          \
    gTc->AddRecord(new TesterRecord(std::string(#s1)+"!="+#s2, __LINE__, \
    strlen((char*)s1)!=strlen((char*)s2) || memcmp(s1, s2, strlen((char*)s1)) != 0))

#define EXPECT_FILESIZE(path, size) {                                   \
    struct stat st;                                                     \
    int fd = open(path, O_RDONLY, 0644);                                \
    fstat(fd, &st);                                                     \
    gTc->AddRecord(new TesterRecord(                                    \
        std::string("File(")+#path+") size is "+#size, __LINE__,        \
        st.st_size==size));                                             \
    close(fd);                                                          \
}

#define EXPECT_FILESIZE_FD(fd, size) {                                  \
    struct stat st;                                                     \
    fstat(fd, &st);                                                     \
    gTc->AddRecord(new TesterRecord(                                     \
        std::string("File(")+#fd+") size is "+#size, __LINE__,          \
        st.st_size==size));                                             \
}

}

#endif
